package org.lc.oauth.listener;

import cn.hutool.core.util.ObjUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.lc.oauth.vo.LcUserDetails;
import org.lc.platform.base.constant.AuthConstant;
import org.lc.platform.base.constant.LcConstant;
import org.lc.platform.redis.service.CacheService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * 当登录失败时的调用，当密码错误过多时，则锁定账户
 *
 * @author lc
 **/
@Slf4j
@Component
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class LoginFailureListener implements ApplicationListener<AuthenticationFailureBadCredentialsEvent> {

    private final UserDetailsService userDetailsService;

    private final CacheService cache;

    /**
     * 超过尝试次数，锁定账户
     */
    @Value("${properties.oauth2.maxTryTimes:5}")
    private int maxTryTimes = 5;

    /**
     * 锁定时间，单位 秒
     */
    @Value("${properties.oauth2.maxLockDuration:120}")
    private int maxLockDuration = 120;

    @Override
    public void onApplicationEvent(AuthenticationFailureBadCredentialsEvent event) {

        if (!(event.getException() instanceof BadCredentialsException)) {
            return;
        }
        try {
            String userName = event.getAuthentication().getName();
            var user = (LcUserDetails) userDetailsService.loadUserByUsername(userName);
            if (ObjUtil.isNotNull(user)) {
                /* 从Redis获取账号密码错误次数 */
//                var key = AuthConstant.LOCK_ACCOUNT_PREFIX + user.getUserId();
                var key = user.getUserId() + ":account_lock";
                var lockTimes = this.cache.getInt(key, 0);
                if (ObjUtil.isNull(lockTimes) || lockTimes < maxTryTimes) {
                    this.cache.getRedis().boundValueOps(key).increment(LcConstant.Number.ONE);
                    this.cache.getRedis().expire(key, maxLockDuration, TimeUnit.SECONDS);
                }
            }
        } catch (Exception e) {
            log.error("增加账户锁定次数失败：{0}", e);
        }
    }
}